﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using Newtonsoft.Json;
using Xwltz.Framework.Framework;

namespace Xwltz.Framework.Cloud
{
    public class ReportService
    {
        private readonly LoginService _login;
        private DoLoginResponse _loginResponse;

        private readonly string _listUrl = FrameworkConfigs.ReportUrl + "/getSiteList";
        private readonly string _dataUrl = FrameworkConfigs.ReportUrl + "/getData";

        public ReportService(LoginService login)
        {
            _login = login;
        }

        public string GetSiteList()
        {
            try
            {
                if (_loginResponse == null || _loginResponse.ReturnCode != 0) _loginResponse = _login.DoLogin();
                var data = new ReportRequest();
                data.Header.AccountType = FrameworkConfigs.AccountType;
                data.Header.Password = _loginResponse.St;
                data.Header.Token = _login.Token;
                data.Header.UserName = _login.UserName;

                var value = ServiceUnit.RequestReport(_listUrl, _loginResponse.Ucid, data);
                var response = JsonConvert.DeserializeObject<ReportResponse>(value);
                return response.Body.ToString();

            }
            finally
            {
                if (_loginResponse != null && (_loginResponse != null || _loginResponse.ReturnCode == 0)) _login.DoLogout(_loginResponse.Ucid, _loginResponse.St);
            }
        }

        public string GetSiteData(BodyRequest bodyData)
        {
            try
            {
                if (_loginResponse == null || _loginResponse.ReturnCode != 0) _loginResponse = _login.DoLogin();

                var data = new ReportRequest();
                data.Header.AccountType = FrameworkConfigs.AccountType;
                data.Header.Password = _loginResponse.St;
                data.Header.Token = _login.Token;
                data.Header.UserName = _login.UserName;

                data.Body = bodyData;

                var value = ServiceUnit.RequestReport(_dataUrl, _loginResponse.Ucid, data);
                return value;
            }
            finally
            {
                if (_loginResponse != null && (_loginResponse != null || _loginResponse.ReturnCode == 0)) _login.DoLogout(_loginResponse.Ucid, _loginResponse.St);
            }
        }
    }

    public class LoginService
    {
        private readonly Header _header;
        private readonly string _password;
        private RequestModel _request;

        public string UserName
        {
            get => _header.UserName;
            set => _header.UserName = value;
        }
        public string Token
        {
            get => _header.Token;
            set => _header.Token = value;
        }

        public LoginService()
        {
            _header = new Header();
        }

        public LoginService(string userName, string password, string token)
        {
            _header = new Header();
            _header.UserName = userName;
            _header.Token = token;
            _password = password;
        }

        public PreLoginResponse PreLgoin()
        {
            _header.FunctionName = "preLogin";
            _request = new PreLoginRequest();
            _header.Request = _request;

            var value = RequestService<PreLoginResponse>();
            return value;
        }

        public DoLoginResponse DoLogin()
        {
            _header.FunctionName = "doLogin";
            _request = new DoLoginRequest();
            _header.Request = _request;
            ((DoLoginRequest)_request).Password = _password;

            var value = RequestService<DoLoginResponse>();
            return value;
        }

        public DoLogoutResponse DoLogout(long ucid, string st)
        {
            _header.FunctionName = "doLogout";
            _request = new DoLogoutRequest();
            _header.Request = _request;
            ((DoLogoutRequest)_request).Ucid = ucid;
            ((DoLogoutRequest)_request).St = st;

            var value = RequestService<DoLogoutResponse>();
            return value;
        }

        public T RequestService<T>()
        {
            var value = ServiceUnit.RequestLogin(FrameworkConfigs.LoginUrl, FrameworkConfigs.AccountType, _header);
            var response = JsonConvert.DeserializeObject<T>(value);
            return response;
        }
    }

    public class ServiceUnit
    {
        #region http

        public static string RequestLogin(string uri, string accountType, Header jsonData)
        {
            jsonData.Uuid = CommonBase.GuId(true);
            var json = JsonConvert.SerializeObject(jsonData);
            var zipData = Compression(json);

            var postData = Encrypt(zipData);

            var responseData = RequestLogin(uri, jsonData.Uuid, accountType, postData);

            // 解压gzip 从第8个字节开始是正式数据
            var value = Decompress(responseData.Skip(8).Take(responseData.Length - 8).ToArray());
            return value;
        }

        public static string RequestReport(string uri, long userId, ReportRequest jsonData)
        {
            var json = JsonConvert.SerializeObject(jsonData);
            var bytes = Encoding.UTF8.GetBytes(json);

            var responseData = RequestReport(uri, userId, bytes);
            var value = Encoding.UTF8.GetString(responseData);

            return value;
        }

        public static byte[] RequestLogin(string uri, string uuid, string accountType, byte[] postData)
        {
            var request = WebRequest.Create(uri);
            request.Method = "POST";
            request.ContentType = "data/gzencode and rsa public encrypt;charset=UTF-8";
            request.Headers.Add("UUID", uuid);
            request.Headers.Add("ACCOUNT_TYPE", accountType);

            var myRequestStream = request.GetRequestStream();
            myRequestStream.Write(postData, 0, postData.Length);
            myRequestStream.Close();

            var response = request.GetResponse();
            var responseStream = response.GetResponseStream();

            var count = (int)response.ContentLength;
            var offset = 0;
            var buf = new byte[count];
            while (count > 0)
            {
                if (responseStream == null) continue;
                var n = responseStream.Read(buf, offset, count);
                if (n == 0) break;
                count -= n;
                offset += n;
            }
            response.Close();

            return buf;
        }

        public static byte[] RequestReport(string uri, long userId, byte[] postData)
        {
            var request = WebRequest.Create(uri);
            request.Method = "POST";
            request.ContentType = "data/json;charset=UTF-8";
            request.Headers.Add("UUID", CommonBase.GuId(true));
            request.Headers.Add("USERID", userId.ToString());

            var myRequestStream = request.GetRequestStream();
            myRequestStream.Write(postData, 0, postData.Length);
            myRequestStream.Close();

            var response = request.GetResponse();
            var responseStream = response.GetResponseStream();

            var bytes = new List<byte>();
            if (responseStream != null)
            {
                var temp = responseStream.ReadByte();
                while (temp != -1)
                {
                    bytes.Add((byte)temp);
                    temp = responseStream.ReadByte();
                }
            }
            response.Close();

            return bytes.ToArray();
        }

        #endregion

        #region 压缩

        public static byte[] Compression(string text)
        {
            byte[] cbytes;
            using (var cms = new MemoryStream())
            {
                using (var gzip = new GZipStream(cms, CompressionLevel.Optimal, false))
                {
                    var bytes = Encoding.UTF8.GetBytes(text);
                    gzip.Write(bytes, 0, bytes.Length);
                }
                cbytes = cms.ToArray();
            }
            return cbytes;
        }

        public static string Decompress(byte[] buffer)
        {
            using (var dms = new MemoryStream())
            {
                using (var cms = new MemoryStream(buffer))
                {
                    using (var gzip = new GZipStream(cms, CompressionMode.Decompress))
                    {
                        var bytes = new byte[1024];
                        int len;
                        while ((len = gzip.Read(bytes, 0, bytes.Length)) > 0)
                        {
                            dms.Write(bytes, 0, len);
                        }
                    }
                }
                return Encoding.UTF8.GetString(dms.ToArray());
            }
        }

        #endregion

        #region RSA加密

        public static byte[] Encrypt(byte[] data)
        {
            var n = (int)Math.Ceiling(data.Length * 1.0 / 117);
            var ret = new byte[n * 128];
            for (var i = 0; i < n; i++)
            {
                var length = 117;
                if (i == n - 1) length = data.Length - i * 117;
                var gzdata = new byte[length];
                Array.Copy(data, i * 117, gzdata, 0, length);
                Array.Copy(RsaEncrypt(gzdata), 0, ret, i * 128, 128);
            }
            return ret;
        }

        public static byte[] RsaEncrypt(byte[] data)
        {
            var rsa = new RSACryptoServiceProvider();

            rsa.FromXmlString("<RSAKeyValue><Modulus>x5/4X70y0V1YgV05gYTWBCCXhhhg5Jq2AcQkXpa4ixGBHK8Fq86dGrvZxCEqYzJV3hFuPk/NmwjNtviVF2aSIGuanQaz5I1NPCrX+Z+zbHe5/WXiNGKACgrm97pVRMXMRnkt0VcRWovaMxEZWr6aEznVMxB+mFhz+K3hX6TKS88=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>");
            var cipherbytes = rsa.Encrypt(data, false);
            rsa.Clear();
            return cipherbytes;
        }

        #endregion
    }

    public class RefererService
    {
        private const string Host = "referer.bj.baidubce.com";
        private static readonly string ApiUri = $"http://{Host}/v1/eqid/";
        private const string AccessKey = "c203402a654b43e0aeb9bd7cfb95d986";
        private const string SecretKey = "63a8b60b3eac4e9dae017c55bceb3066";

        /// <summary>
        /// 
        /// </summary>
        /// <param name="eqid"></param>
        /// <returns></returns>
        public static string GetKeyWord(string eqid)
        {
            var utcTimeStr = GetUtcTime();
            if (WebRequest.Create(ApiUri + eqid) is HttpWebRequest request)
            {
                request.Method = "GET";
                request.Accept = "*/*";
                request.Host = Host;
                request.ContentType = "application/json";

                request.Headers.Add("accept-encoding", "gzip, deflate");
                request.Headers.Add("x-bce-date", utcTimeStr);
                request.Headers.Add("authorization", GetAuth(request, utcTimeStr));

                var response = (HttpWebResponse)request.GetResponse();
                var myResponseStream = response.GetResponseStream();
                if (myResponseStream == null) return "";
                var myStreamReader = new StreamReader(myResponseStream, Encoding.GetEncoding("utf-8"));

                var retString = myStreamReader.ReadToEnd();
                return retString;
            }
            return "";
        }

        private static string GetAuth(HttpWebRequest request, string utcTimeStr)
        {
            string authString = $"bce-auth-v1/{AccessKey}/{utcTimeStr}/1800";
            string signingKey = Hex(new HMACSHA256(Encoding.UTF8.GetBytes(SecretKey)).ComputeHash(Encoding.UTF8.GetBytes(authString)));
            Console.WriteLine("[signingKey]：" + signingKey);

            string canonicalRequestString = CanonicalRequest(request, utcTimeStr);
            Console.WriteLine("[canonicalRequestString]：" + canonicalRequestString);

            string signature = Hex(new HMACSHA256(Encoding.UTF8.GetBytes(signingKey)).ComputeHash(Encoding.UTF8.GetBytes(canonicalRequestString)));
            string authorization = authString + "/host;x-bce-date/" + signature;
            Console.WriteLine("[authorization]：" + authorization);
            return authorization;
        }

        private static string GetUtcTime()
        {
            DateTime now = DateTime.Now;
            string signDate = now.ToUniversalTime().ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ssK");
            Console.WriteLine(signDate);
            return signDate;
        }

        private static string UriEncode(string input, bool encodeSlash = false)
        {
            StringBuilder builder = new StringBuilder();
            foreach (byte b in Encoding.UTF8.GetBytes(input))
            {
                if ((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9') || b == '_' || b == '-' || b == '~' || b == '.')
                {
                    builder.Append((char)b);
                }
                else if (b == '/')
                {
                    if (encodeSlash)
                    {
                        builder.Append("%2F");
                    }
                    else
                    {
                        builder.Append((char)b);
                    }
                }
                else
                {
                    builder.Append('%').Append(b.ToString("X2"));
                }
            }
            return builder.ToString();
        }

        private static string Hex(byte[] data)
        {
            var sb = new StringBuilder();
            foreach (var b in data)
            {
                sb.Append(b.ToString("x2"));
            }
            return sb.ToString();
        }

        private static string CanonicalRequest(HttpWebRequest req, string utcTimeStr)
        {
            Uri uri = req.RequestUri;
            StringBuilder canonicalReq = new StringBuilder();
            canonicalReq.Append(req.Method).Append("\n").Append(UriEncode(Uri.UnescapeDataString(uri.AbsolutePath))).Append("\n");

            var parameters = HttpUtility.ParseQueryString(uri.Query);
            var parameterStrings = new List<string>();
            foreach (KeyValuePair<string, string> entry in parameters)
            {
                parameterStrings.Add(UriEncode(entry.Key) + '=' + UriEncode(entry.Value));
            }
            parameterStrings.Sort();
            canonicalReq.Append(string.Join("&", parameterStrings.ToArray())).Append("\n");
            var host = uri.Host;
            if (!(uri.Scheme == "https" && uri.Port == 443) && !(uri.Scheme == "http" && uri.Port == 80))
            {
                host += ":" + uri.Port;
            }   
            canonicalReq.Append("host:" + UriEncode(host)).Append("\n");
            canonicalReq.Append("x-bce-date:" + UriEncode(utcTimeStr));
            return canonicalReq.ToString();
        }
    }

    #region LoginModels

    #region Common

    public class Header
    {
        [JsonProperty("username")]
        public string UserName { get; set; }

        [JsonProperty("token")]
        public string Token { get; set; }

        [JsonProperty("functionName")]
        public string FunctionName { get; set; }

        [JsonProperty("uuid")]
        public string Uuid { get; set; }

        [JsonProperty("request")]
        public RequestModel Request { get; set; }
    }

    public class AuthCode
    {
        [JsonProperty("imgtype")]
        public string ImageType { get; set; }

        [JsonProperty("imgdata")]
        public string ImageData { get; set; }

        [JsonProperty("imgssid")]
        public string ImageServiceSetIdentifier { get; set; }
    }

    public class Question
    {
        [JsonProperty("qid")]
        public int QuestionId { get; set; }

        [JsonProperty("content")]
        public string Content { get; set; }
    }

    #endregion

    #region Login Init

    /// <summary>
    /// 初始化登陆请求
    /// </summary>
    public class PreLoginRequest : RequestModel
    {
        [JsonProperty("osVersion")]
        public string OsVersion { get; set; }

        [JsonProperty("deviceType")]
        public string DeviceType { get; set; }

        [JsonProperty("clientVersion")]
        public string ClientVersion { get; set; }

        public PreLoginRequest()
        {
            ClientVersion = "1.0";
            OsVersion = "windows";
            DeviceType = "pc";
        }
    }

    /// <summary>
    /// 初始化登陆响应
    /// </summary>
    public class PreLoginResponse
    {
        [JsonProperty("needAuthCode")]
        public bool NeedAuthCode { get; set; }

        [JsonProperty("authCode")]
        public AuthCode AuthCode { get; set; }
    }

    #endregion

    #region Login

    /// <summary>
    /// 登录请求
    /// </summary>
    public class DoLoginRequest : RequestModel
    {
        [JsonProperty("password")]
        public string Password { get; set; }
    }

    /// <summary>
    /// 登陆响应
    /// </summary>
    public class DoLoginResponse
    {
        [JsonProperty("retcode")]
        public int ReturnCode { get; set; }

        [JsonProperty("retmsg")]
        public string ReturnMessage { get; set; }

        [JsonProperty("ucid")]
        public long Ucid { get; set; }

        [JsonProperty("st")]
        public string St { get; set; }

        [JsonProperty("istoken")]
        public int IsToken { get; set; }

        [JsonProperty("setpin")]
        public int SetPin { get; set; }

        [JsonProperty("questions")]
        public Question[] Questions { get; set; }
    }

    #endregion

    #region Body

    /// <summary>
    /// 请求体
    /// </summary>
    public class RequestModel
    {

    }

    #endregion

    #region Logout

    /// <summary>
    /// 登出请求
    /// </summary>
    public class DoLogoutRequest : RequestModel
    {
        [JsonProperty("ucid")]
        public long Ucid { get; set; }

        [JsonProperty("st")]
        public string St { get; set; }
    }

    /// <summary>
    /// 登出响应
    /// </summary>
    public class DoLogoutResponse
    {
        [JsonProperty("retcode")]
        public int ReturnCode { get; set; }

        [JsonProperty("retmsg")]
        public string ReturnMessage { get; set; }
    }
    #endregion

    #endregion

    #region DataApiModels

    public class ReportRequest
    {
        [JsonProperty("header")]
        public HeaderRequest Header { get; set; }

        [JsonProperty("body")]
        public BodyRequest Body { get; set; }

        public ReportRequest()
        {
            Header = new HeaderRequest();
            Body = null;
        }
    }

    public class ReportResponse
    {
        [JsonProperty("header")]
        public HeaderResponse Header { get; set; }

        [JsonProperty("body")]
        public object Body { get; set; }
    }

    public class HeaderRequest
    {
        [JsonProperty("username")]
        public string UserName { get; set; }

        [JsonProperty("token")]
        public string Token { get; set; }

        [JsonProperty("password")]
        public string Password { get; set; }

        [JsonProperty("account_type")]
        public string AccountType { get; set; }
    }

    public class HeaderResponse
    {
        [JsonProperty("desc")]
        public string Desc { get; set; }

        [JsonProperty("failures")]
        public object[] Failures { get; set; }

        [JsonProperty("oprs")]
        public int Oprs { get; set; }

        [JsonProperty("succ")]
        public int Succ { get; set; }

        [JsonProperty("oprtime")]
        public int Oprtime { get; set; }

        [JsonProperty("quota")]
        public int Quota { get; set; }

        [JsonProperty("rquota")]
        public int Rquota { get; set; }

        [JsonProperty("status")]
        public int Status { get; set; }
    }

    public class BodyRequest
    {
        #region 基本参数

        /// <summary>
        /// 站点id
        /// </summary>
        [JsonProperty("site_id")]
        public uint SiteId { set; get; }

        /// <summary>
        /// 通常对应要查询的报告
        /// </summary>
        [JsonProperty("method")]
        public string Method { set; get; }

        /// <summary>
        /// 查询起始时间
        /// </summary>
        [JsonProperty("start_date")]
        public string StartDate { set; get; }

        /// <summary>
        /// 查询结束时间
        /// </summary>
        [JsonProperty("end_date")]
        public string EndDate { set; get; }

        /// <summary>
        /// 对比查询起始时间
        /// </summary>
        [JsonProperty("start_date2")]
        public string StartDate2 { set; get; }

        /// <summary>
        /// 对比查询结束时间
        /// </summary>
        [JsonProperty("end_date2")]
        public string EndDate2 { set; get; }

        /// <summary>
        /// 自定义指标选择，多个指标用逗号分隔
        /// </summary>
        [JsonProperty("metrics")]
        public string Metrics { get; set; }

        /// <summary>
        /// 时间粒度 day/hour/week/month
        /// </summary>
        [JsonProperty("gran")]
        public string Gran { get; set; }

        /// <summary>
        /// 指标排序
        /// </summary>
        [JsonProperty("order")]
        public string Order { get; set; }

        /// <summary>
        /// 获取数据偏移,用于分页;默认0
        /// </summary>
        [JsonProperty("start_index")]
        public uint StartIndex { get; set; }

        /// <summary>
        /// 单词获取数据条数，用于分页；默认20；0表示获取所有数据。
        /// </summary>
        [JsonProperty("max_results")]

        private uint _maxResults = 20;
        public uint MaxResults
        {
            get => _maxResults;
            set => _maxResults = value;
        }

        #endregion

        #region 筛选参数



        #endregion
    }

    public class BodyResponse
    {

    }

    #endregion
}
